﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.IO;
using Crude.Common;
using Crude.Core;
using Crude.Core.Repository;

namespace Crude.Tasks
{
    public class UpdateLocalRepository : Task
    {
        [Required]
        public string ProjectDirectory { get; set; }

        [Required]
        public string ProjectName { get; set; }

        [Required]
        public string Configuration { get; set; }

        [Required]
        public bool OverrideNewerAssemblies { get; set; }

        [Required]
        public ITaskItem[] ProjectReferences { get; set; }

        public override bool Execute()
        {
            Log.LogMessage("Update local repository started...");        

            string pomFile = string.Format("{0}\\{1}.pom.xml", ProjectDirectory, ProjectName);

            IList<ProjectObjectModel> assembliesToDownload = new List<ProjectObjectModel>();

            if (!File.Exists(pomFile))
            {
                Log.LogError("Pom file or project {0} is missing, please generate pom file {1}", ProjectName, pomFile);
                return false;
            }

            HashSet<string> projectReferencesNames = new HashSet<string>
                (from projectReference in ProjectReferences
                 select projectReference.GetMetadata("Name"));

            ProjectObjectModel mainPom = new ProjectObjectModelLoader(pomFile).Load();

            LocalRepository localRepository = new LocalRepositoryBuilder().Build();
            IRemoteRepository remoteRepository = new RemoteRepositoryBuilder().Build();

            IDictionary<string, ProjectObjectModel> solvedAssemblies = new Dictionary<string, ProjectObjectModel>();
            solvedAssemblies.Add(mainPom.Name, mainPom);

            Queue<ProjectDependency> dependencyToSolve = new Queue<ProjectDependency>();

            foreach (ProjectDependency dependency in mainPom.Dependencies)
            {
                dependencyToSolve.Enqueue(dependency);
            }

            while (dependencyToSolve.Count > 0)
            {
                ProjectDependency dependency = dependencyToSolve.Dequeue();

                if (!projectReferencesNames.Contains(dependency.Name))
                {
                    ProjectObjectModel localPom = localRepository.GetProjectObjectModel(dependency);
                    ProjectObjectModel remotePom = remoteRepository.GetProjectObjectModel(dependency);

                    DateTime remotePomUpdateTime = remoteRepository.GetProjectBuildDate(remotePom, Configuration);
                    DateTime localPomUpdateTime = DateTime.MinValue;

                    if (localPom != null)
                    {
                        localPomUpdateTime = localRepository.GetProjectBuildDate(localPom, Configuration);
                    }

                    // if assembly dont exist on local repository or if the remote versio is newer than the local one
                    // or if the remote last update time is newer than the local one
                    if ((localPom == null) || 
                        (new Version(remotePom.Version) > new Version(localPom.Version)) ||
                        (remotePomUpdateTime > localPomUpdateTime) ||
                        (remotePomUpdateTime != localPomUpdateTime && OverrideNewerAssemblies))
                    {
                        if (!assembliesToDownload.Contains(remotePom))
                        {
                            assembliesToDownload.Add(remotePom);
                        }
                    }

                    if (!solvedAssemblies.ContainsKey(remotePom.Name))
                    {
                        foreach (ProjectDependency subDependency in remotePom.Dependencies)
                        {
                            dependencyToSolve.Enqueue(subDependency);
                        }

                        solvedAssemblies.Add(remotePom.Name, remotePom);
                    }
                    else if (!solvedAssemblies[remotePom.Name].Version.Equals(remotePom.Version))
                    {
                        Log.LogError("Assemblies conflict {0} between version {1} to version {2}", remotePom.Name,
                            remotePom.Version, solvedAssemblies[remotePom.Name].Version);
                        return false;
                    }
                }
            }           

            foreach (ProjectObjectModel pom in assembliesToDownload)
            {
                Log.LogMessage("Downloading {0} version {1}", pom.Name, pom.Version);
                remoteRepository.DownloadAssmebly(pom);
            }

            Log.LogMessage("Complete downloading assemblies");

            Log.LogMessage("Update local repository succeeded");

            return true;
        }
    }
}
